import axios, { AxiosResponse } from 'axios'
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
import type { RequestConfig, RequestInterceptors, CreateRequestConfig, SessionObj } from './types'
import { getToken } from '@/utils/auth'
import { tansParams } from '@/utils'
import cache from '@/plugins/cache'

class Request {
  // axios 实例
  instance: AxiosInstance
  // 拦截器对象
  interceptorsObj?: RequestInterceptors<AxiosResponse>
  // * 存放取消请求控制器Map
  abortControllerMap: Map<string, AbortController>

  constructor(config: CreateRequestConfig) {
    this.instance = axios.create(config)
    // * 初始化存放取消请求控制器Map
    this.abortControllerMap = new Map()
    this.interceptorsObj = config.interceptors

    // 拦截器执行顺序 接口请求 -> 实例请求 -> 全局请求 -> 实例响应 -> 全局响应 -> 接口响应
    // 请求拦截
    this.interceptorsRequest()
    // 使用实例拦截器
    this.instance.interceptors.request.use(
      this.interceptorsObj?.requestInterceptors,
      this.interceptorsObj?.requestInterceptorsCatch
    )
    this.instance.interceptors.response.use(
      this.interceptorsObj?.responseInterceptors,
      this.interceptorsObj?.responseInterceptorsCatch
    )
    // 响应拦截
    this.interceptorsResponse()
  }

  // 请求拦截
  interceptorsRequest() {
    this.instance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 是否需要设置 token
        const isToken = (config.headers || {}).isToken === false
        // 是否需要防止数据重复提交
        const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
        if (getToken() && !isToken) {
          if (config.headers) {
            config.headers.Authorization = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
          }
        }
        //get请求映射params参数
        if (config.method === 'get' && config.params) {
          let url = config.url + '?' + tansParams(config.params)
          url = url.slice(0, -1)
          config.params = {}
          config.url = url
        }
        // post与put重复提交处理
        if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
          const requestObj = {
            url: config.url,
            data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
            time: new Date().getTime(),
          }
          const sessionObj: SessionObj = cache.session.get('sessionObj')
          if (sessionObj === undefined || sessionObj === null) {
            cache.session.set('sessionObj', requestObj)
          } else {
            const sUrl = sessionObj.url // 请求地址
            const sData = sessionObj.data // 请求数据
            const sTime = sessionObj.time // 请求时间
            const interval = 0 // 间隔时间(ms)，小于此时间视为重复提交
            if (
              sData === requestObj.data &&
              requestObj.time - sTime < interval &&
              sUrl === requestObj.url
            ) {
              const message = '数据正在处理，请勿重复提交'
              // console.warn(`[${sUrl}]: ` + message)
              return Promise.reject(new Error(message))
            }
            cache.session.set('sessionObj', requestObj)
          }
        }
        // 取消请求
        const controller = new AbortController()
        const url = config.url || ''
        config.signal = controller.signal
        this.abortControllerMap.set(url, controller)
        return config
      },
      (err: any) => err
    )
  }

  //  响应拦截：全局响应拦截器保证最后执行
  interceptorsResponse() {
    this.instance.interceptors.response.use(
      // 因为我们接口的数据都在res.data下，所以我们直接返回res.data
      (res: AxiosResponse) => {
        const url = res.config.url || ''
        this.abortControllerMap.delete(url)
        return res.data
      },
      (err: any) => err
    )
  }

  request<T>(config: RequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 如果我们为单个请求设置拦截器，这里使用单个请求的拦截器
      if (config.interceptors?.requestInterceptors) {
        config = config.interceptors.requestInterceptors(config as any)
      }
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 如果我们为单个响应设置拦截器，这里使用单个响应的拦截器
          if (config.interceptors?.responseInterceptors) {
            res = config.interceptors.responseInterceptors(res)
          }

          resolve(res)
        })
        .catch((err: any) => {
          reject(err)
        })
      // .finally(() => {})
    })
  }
  /**
   * 取消全部请求
   */
  cancelAllRequest() {
    for (const [, controller] of this.abortControllerMap) {
      controller.abort()
    }
    this.abortControllerMap.clear()
  }
  /**
   * 取消指定的请求
   * @param url 待取消的请求URL
   */
  cancelRequest(url: string | string[]) {
    const urlList = Array.isArray(url) ? url : [url]
    for (const _url of urlList) {
      this.abortControllerMap.get(_url)?.abort()
      this.abortControllerMap.delete(_url)
    }
  }
}

export default Request
export { RequestConfig, RequestInterceptors }
